#pragma once

#ifdef WIN32
#define FUNCTION __FUNCTION__
#define FUNCSIG __FUNCSIG__
#else
#define FUNCTION __PRETTY_FUNCTION__
#define FUNCSIG __PRETTY_FUNCTION__
#endif

namespace Alg
{

    typedef void (*LoggerCallback_t)( const char * msg, void * user_data);

class Logger
{
public:
	enum Level 
	{
		Silent = 0,
		Error,
		Warning,
		Info,
            Verbose,
            Debug
	};

    private:
        LoggerCallback_t _callback;
        void * _user_data;

	Level _level;
	bool _enable_thread_id;
	bool _enable_timestamp;
	bool _enable_prefix;
	
public:
        static Logger s_logger;
	Logger();

	void SetLevel(Level level_);
        void Set(LoggerCallback_t callback, void * user_data);
        void Set(LoggerCallback_t callback, void * user_data, Level level);
        //void Get(LoggerCallback_t & callback, void * & user_data); // blame yourself if you have ever thought about using this method!!

        void SetEnableThreadId(bool enabled);
        void SetEnableTimestamp(bool enabled);
        void SetEnablePrefix(bool enabled);

        /**
        * This method is thread-unsafe
        */
        void SetStd(); // for internal use

        void Write(Level level, const std::string & message) const;
    private:
	static const char *GetPrefix(Level level_);

#ifdef WIN32
	void DumpMemoryUsage();
#endif
};


#define ALG_LOG(level, msg) \
    Alg::Logger::s_logger.Write(Alg::Logger::level, msg);

#define ALG_LOG_FMT(level, fmt, args) \
    Alg::Logger::s_logger.Write(Alg::Logger::level, (boost::format(fmt) % args).str());


#define ALG_LOG_SS(level, msg) \
	{ \
		std::stringstream ___ss; \
		___ss << msg; \
    Alg::Logger::s_logger.Write(Alg::Logger::level, ___ss.str()); \
	}


#define ALG_LOG_OBJECT(level, object) \
{ \
	std::stringstream ___ss; \
	boost::archive::xml_oarchive ___oa(___ss); \
	___oa & BOOST_SERIALIZATION_NVP(object); \
    Alg::Logger::s_logger.Write(Alg::Logger::level, ___ss.str()); \
}

#define ALG_LOG_HRESULT(hr) \
	ALG_LOG_FMT(Error, "COM error 0x%08x: %s", hr % _com_error(hr).ErrorMessage());


#define ALG_CONTEXT  __FILE__ << "(" << __LINE__ << ") : '" << FUNCTION << "': "

#define ALG_LOG_CONTEXT(level, msg) \
	ALG_LOG_SS(level, ALG_CONTEXT << msg);

#define ALG_LOG_CONTEXT_FMT(level, fmt, args) \
	ALG_LOG_CONTEXT(level, (boost::format(fmt) %  args));

#define ALG_LOG_CONTEXT_HRESULT(hr) \
    ALG_LOG_CONTEXT_FMT(Error, "COM error 0x%08x: %s", hr % _com_error(hr).ErrorMessage());


    struct Scope 
    {
        Scope(const char * ctx_);
        ~Scope();
        const char * _ctx;
    };

// It should be written in the beginning of a function's body. Its level is Verbose. 
#define ALG_LOG_FUNC \
    Alg::Scope __func_scope__( FUNCTION );

}

